package com.spring.nio.core.server;

import com.spring.nio.business.file.service.FileService;
import com.spring.nio.business.file.service.dto.FileDto;
import com.spring.nio.business.user.dto.UserDTO;
import com.spring.nio.common.BasicConstant;
import com.spring.nio.core.channel.handler.AbstractChannelHandler;
import com.spring.nio.core.configuration.NioServerBootstrap;
import com.spring.nio.core.startup.BasicServer;
import com.spring.nio.service.file.model.structure.FileTransportTaskDTO;
import com.spring.nio.service.file.task.FileDownloadStreamCallable;
import com.spring.nio.util.LocalTime;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @author: Spring
 * @date: 2021-04-24 23:34
 * @package_name: com.spring.nio.core.server
 * @class_name: FileSystemServer
 * @description: File 系统服务
 */
@Slf4j
public class FileSystemServer {

    public static void initializeFileFolder() {
        String basePath = "", osName = BasicServer.getMap().get(BasicConstant.OS_NAME).toString();
        if(StringUtils.contains(osName, "Win")) {
            if(BasicServer.getMap().containsKey(BasicConstant.NIO_FILE_BASE_PATH_WINDOWS)) {
                basePath = BasicServer.getMap().get(BasicConstant.NIO_FILE_BASE_PATH_WINDOWS).toString();
            }
        } else {
            if(BasicServer.getMap().containsKey(BasicConstant.NIO_FILE_BASE_PATH_LINUX_MAC)) {
                basePath = BasicServer.getMap().get(BasicConstant.NIO_FILE_BASE_PATH_LINUX_MAC).toString();
            }
        }

        if(StringUtils.equals(basePath, "")) {
            throw new RuntimeException("请在server.properties文件中配置文件保存路径");
        }

        // 文件末尾是否含有分隔符
        basePath = basePath.replace(" ", "").endsWith(File.separator)?basePath:basePath+File.separator;

        // 获取所有的文件夹信息
        final String rootPath = basePath;
        List<FileDto> fileDtoList = NioServerBootstrap.getConfig().getClassPathXmlApplicationContext().getBean(FileService.class).getAllFileFolder();
        fileDtoList.stream().forEach(fileDto -> {
            fileDto.setFilePath(StringUtils.equals(osName, "Win")?fileDto.getFilePath():fileDto.getFilePath().replaceAll("\\\\", "/"));
            String folderPath = rootPath + fileDto.getFilePath();
            File loopFile = new File(folderPath);
            if(!loopFile.exists()) {
                loopFile.mkdirs();
                log.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] FileSystemServer | --> 文件夹 [{}] 路径 [{}] 已重新构建", fileDto.getFileName(), loopFile.getPath());
            }
        });
    }

    /**
     * 创建文件
     * @param filePath
     * @return File
     * */
    public static File createFile(String filePath) throws IOException {
        File file = new File(filePath);
        if(!file.exists()) {
            file.createNewFile();
            return file;
        }

        return null;
    }

    /**
     * 创建文件夹
     * @param filePath
     * @return
     * */
    public static Boolean createFolder(String filePath) {
        File file = new File(filePath);
        if(!file.exists()) {
            file.mkdirs();
        }

        return Boolean.TRUE;
    }

    /**
     * 验证文件是否存在
     * @param filePath
     * */
    public static Boolean isExist(String filePath) {
        if(StringUtils.isBlank(filePath)) {
            return Boolean.FALSE;
        }

        return new File(filePath).exists();
    }

    /**
     * 关闭文件通道
     * @param fileChannel
     * @return
     * */
    public static Boolean closeFileChannel(FileChannel fileChannel) throws IOException {
        if(!Objects.isNull(fileChannel) && fileChannel.isOpen()) {
            fileChannel.close();
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    /**
     * 完成文件任务上传下载
     * @param remoteAddress
     * @param userDto
     * @param isClearSingleFileUploadTask
     * @param isClearSingleFileDownloadTask
     * */
    public static void finishFileTransportTask(String remoteAddress, UserDTO userDto, Boolean isClearSingleFileUploadTask, Boolean isClearSingleFileDownloadTask) {
        if(isClearSingleFileUploadTask) {
            finishFileUploadTask(remoteAddress, userDto, isClearSingleFileUploadTask);
        }

        if(isClearSingleFileDownloadTask) {
            finishFileDownloadTask(remoteAddress, userDto, isClearSingleFileDownloadTask);
        }
    }

    private static void finishFileUploadTask(String remoteAddress, UserDTO userDto, final Boolean isClearSingleFileUploadTask) {
        // 只清除当前remoteAddress对应的文件上传任务
        if(!Objects.isNull(userDto) && !CollectionUtils.isEmpty(userDto.getUploadFileMap())) {
            if(isClearSingleFileUploadTask) {
                closeFileUploadTask(userDto.getUploadFileMap().get(remoteAddress));
            } else {
                userDto.getUploadFileMap().values().stream().forEach(fileTransportTaskDto -> { closeFileUploadTask(fileTransportTaskDto);});
            }
        }
    }

    public static void closeFileUploadTask(FileTransportTaskDTO fileTransportTaskDto) {
        if(!Objects.isNull(fileTransportTaskDto)) {
            // 上传成功，释放待上传文件占用资源,即删除userDto中map对应的key-value
            try {
                FileSystemServer.closeFileChannel(fileTransportTaskDto.getFileChannel());
                fileTransportTaskDto.getSocketChannelContext().getSocketChannel().close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                log.info("[" + Thread.currentThread().getName() + "] [ " + LocalTime.formatDate(LocalDateTime.now()) + " ] 已成功关闭用户 = [ " + fileTransportTaskDto.getUserName() + " ] 文件 = [ " + fileTransportTaskDto.getFile().getName() + " ] 上传通道[" + fileTransportTaskDto.getSocketChannelContext().getRemoteAddress() + "]和文件写入通道");
            }
        }
    }

    private static void finishFileDownloadTask(String remoteAddress, UserDTO userDto, Boolean isClearSingleFileDownloadTask) {
        if(!CollectionUtils.isEmpty(AbstractChannelHandler.fileDownloadThreadMap)) {
            if(isClearSingleFileDownloadTask) {
                closeFileDownloadTask(AbstractChannelHandler.fileDownloadThreadMap.get(remoteAddress));
            } else {
                AbstractChannelHandler.fileDownloadThreadMap.values().stream().forEach(map -> { closeFileDownloadTask(map); });
            }
        }
    }

    private static void closeFileDownloadTask(Map<String, Object> map) {
        FileTransportTaskDTO fileTransportTaskDto = (FileTransportTaskDTO) map.get("TASK");
        FileDownloadStreamCallable fileDownloadStreamCallable = (FileDownloadStreamCallable) map.get("CALLABLE");
        if(!Objects.isNull(fileTransportTaskDto)) {
            // 上传成功，释放待上传文件占用资源,即删除userDto中map对应的key-value
            try {
                // 关闭文件下载时的文件读取通道
                FileSystemServer.closeFileChannel(fileTransportTaskDto.getFileChannel());
                // 终止下载线程
                while (!fileDownloadStreamCallable.getStop()) {
                    fileDownloadStreamCallable.setStop(Boolean.TRUE);
                }
                // 关闭文件下载传输通道
                fileTransportTaskDto.getSocketChannelContext().getSocketChannel().close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                log.info("FileSystemServer | --> 已成功关闭文件 = [ " + fileTransportTaskDto.getFile().getName() + "] 下载通道 [" + fileTransportTaskDto.getSocketChannelContext().getRemoteAddress() + "]和文件写入通道");
            }
        }
    }


}